package spinal_loongarch_core132

import spinal.core._
import spinal.lib._

class PipeCtrl extends Bundle with IMasterSlave{
    val valid:Bool = Bool()
    val ex   :Bool = Bool()
    val allow:Bool = Bool()
    val empty:Bool = Bool()
    def this(valid:Bool,ex:Bool){
        this()
        this.valid := valid
        this.ex := ex
    }
    override def asMaster():Unit = {
        out(valid,ex)
        in (allow,empty)
    }
}
class StageBuffer[D<:Bundle](val idat:D) extends Area{
    val valid:Bool = RegInit(False)
    val ex   :Bool = RegInit(False)
    val data :D    = Reg(idat)
    val maygo:Bool = Bool()
    val go   :Bool = Bool()
    val hasex:Bool = Bool()
    // need assignments
    // ready:is ready for next stage
    // newex:generated exception
    val ready:Bool = Bool()
    val newex:Bool = Bool()
    def update(ic:PipeCtrl,oc:PipeCtrl,cancel:Bool){
        go := ic.valid && ready && maygo
        maygo := oc.allow || !valid
        hasex := ex || ic.ex || newex
        ic.allow := ready && maygo && !ex
        ic.empty := oc.empty && !valid
        oc.valid := valid
        oc.ex    := ex
        when(cancel){
            valid := False
            ex    := False
        }.elsewhen(go){
            valid := True
            ex    := hasex
        }.elsewhen(oc.allow){
            valid := False
        }
        when(go){
            data := idat
        }
    }
}
class DoubledStageBuffer[D<:Bundle](val idat:D) extends Area{
    val valid:Vec[Bool] = Vec(RegInit(False),2)
    val ex   :Vec[Bool] = Vec(RegInit(False),2)
    val datas:Vec[D   ] = Vec(Reg(idat),2)
    def data :D    = datas(0)
    val mayin:Bool = Bool()
    val letin:Bool = Bool()
    // need assignments
    // ready:is ready for next stage
    // newex:generated exception
    val ready:Bool = Bool()
    val newex:Bool = Bool()
    def update(ic:PipeCtrl,oc:PipeCtrl,cancel:Bool){
        mayin := ic.valid && ready
        letin := oc.allow || ! valid(0)
        ic.allow := ready && !ex(0) && !valid(1)
        ic.empty := oc.empty && !valid(0) && !valid(1)
        oc.valid := valid(0)
        oc.ex    := ex   (0)
        when(cancel){
            for(i <- 0 to 1){
                valid(i) := False
                ex   (i) := False
            }
        }.elsewhen(valid(1)){
            when(oc.allow){
                valid(0) := True
                ex   (0) := ex   (1) || ex(0)
                datas(0) := datas(1)
                valid(1) := False
            }
        }.elsewhen(mayin){
            when(letin){
                valid(0) := True
                ex   (0) := ic.ex    || newex || ex(0)
                datas(0) := idat
            }.otherwise{
                valid(1) := True
                ex   (1) := ic.ex    || newex || ex(1)
                datas(1) := idat
            }
        }.elsewhen(valid(0) && oc.allow){
            valid(0) := False
        }
    }
}
